package com.openess.bigsearch.engine.Text;

import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import com.openess.bigsearch.engine.BTree.util.ByteArrays;
import com.openess.bigsearch.engine.utils.HashMap;
import com.openess.bigsearch.engine.utils.Map;

/**
 * 保存文本原文
 * 
 * @author TuTu
 * 
 */
public class Text implements Closeable {
	/**
	 * 头数据所占字节数
	 */
	private int headsize = 4;
	/**
	 * 一个(id,offset)所占字节数
	 */
	private int datasize = 16;
	/**
	 * 头数据保存有id的数量
	 */
	private byte[] head;

	private RandomAccessFile rf1;
	private FileChannel cf1;
	private RandomAccessFile rf2;
	private FileChannel cf2;
	/**
	 * (id, TestRaw)用于建索引，不用于搜索
	 */
	private HashMap<Long, TextRaw> indexmap;
	private HashMap<Long, Long> search;

	public Text(String indexPath, String mode, int version) throws IOException {
		head = new byte[headsize];
		rf1 = new RandomAccessFile(indexPath + "text_key_" + version, "rw");
		cf1 = rf1.getChannel();
		rf2 = new RandomAccessFile(indexPath + "text_raw_" + version, "rw");
		cf2 = rf2.getChannel();
		if (mode.contains("w"))
			indexmap = new HashMap<Long, TextRaw>(500);
		else if (mode.contains("r")) {
			search = new HashMap<Long, Long>(500);
			read();
		}
	}

	/**
	 * 整合成(id: (field1,raw1) (field2,raw2) ....)准备写入
	 * 
	 * @param id
	 * @param fieldname
	 * @param raw
	 */
	public void put(long id, String fieldname, String raw) {
		TextRaw tr = indexmap.get(id);
		if (tr == null) {
			tr = new TextRaw();
			tr.add(fieldname, raw);
			indexmap.put(id, tr);
		} else
			tr.add(fieldname, raw);
	}

	/**
	 * 根据id拿到这个文档对应的所有域文本
	 * 
	 * @param id
	 * @throws IOException
	 */
	public TextRaw get(long id) throws IOException {
		long offset = search.get(id);
		TextRaw tr = new TextRaw();
		tr.read(cf2, offset);
		return tr;
	}

	public void write() throws IOException {
		try {
			writeHead();
			// 域文本的偏移量
			long offset = 0;
			int count = 0;
			for (Map.Entry<Long, TextRaw> en : indexmap.entrySet()) {
				TextRaw tr = en.getValue();
				offset = tr.write(cf2, offset);
				long id = en.getKey();
				writeId(count++, id, offset);
			}
		} finally {
			close();
		}
	}

	/**
	 * 写入(id,offset)
	 * 
	 * @param index
	 *            第几个id
	 * @param id
	 * @param offset
	 *            域文本的偏移量
	 * @throws IOException
	 */
	private void writeId(long index, long id, long offset) throws IOException {
		byte[] data = new byte[datasize];
		ByteArrays.putLong(id, data, 0);
		ByteArrays.putLong(offset, data, 8);
		ByteBuffer buf = ByteBuffer.wrap(data);
		cf1.write(buf, headsize + index * datasize);
	}

	/**
	 * 写入id的个数
	 * 
	 * @throws IOException
	 */
	private void writeHead() throws IOException {
		ByteArrays.putInt(indexmap.size(), head, 0);
		ByteBuffer buf = ByteBuffer.wrap(head);
		cf1.write(buf, 0);
	}

	/**
	 * 将(id, offset)加载进内存，提供搜索服务
	 * 
	 * @throws IOException
	 */
	private void read() throws IOException {
		int size = readHead();
		for (int i = 0; i < size; i++) {
			byte[] data = readId(i);
			long id = ByteArrays.getLong(data, 0);
			long offset = ByteArrays.getLong(data, 8);
			search.put(id, offset);
		}
	}

	/**
	 * 读取头数据，id的个数
	 * 
	 * @return
	 * @throws IOException
	 */
	private int readHead() throws IOException {
		ByteBuffer buf = ByteBuffer.wrap(head);
		cf1.read(buf, 0);
		int size = ByteArrays.getInt(head, 0);
		return size;
	}

	/**
	 * 读取(id,offset)字节格式
	 * 
	 * @param 第几个id
	 * @return
	 * @throws IOException
	 */
	private byte[] readId(long index) throws IOException {
		byte[] data = new byte[datasize];
		ByteBuffer buf = ByteBuffer.wrap(data);
		cf1.read(buf, headsize + datasize * index);
		return data;
	}

	@Override
	public void close() throws IOException {
		cf1.close();
		rf1.close();
		cf1.close();
		rf2.close();
	}
}
